package com.tianji.remark.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.common.autoconfigure.mq.RabbitMqHelper;
import com.tianji.common.utils.StringUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.remark.constans.LikeRecordConstans;
import com.tianji.remark.domain.dto.LikeRecordFormDTO;
import com.tianji.remark.domain.dto.LikedTimesDTO;
import com.tianji.remark.domain.po.LikedRecord;
import com.tianji.remark.mapper.LikedRecordMapper;
import com.tianji.remark.service.ILikedRecordService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.connection.StringRedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import static com.tianji.common.constants.MqConstants.Exchange.LIKE_RECORD_EXCHANGE;
import static com.tianji.common.constants.MqConstants.Key.LIKED_TIMES_KEY_TEMPLATE;
import static com.tianji.remark.constans.LikeRecordConstans.LIKE_TIME_TYPE_PRIFIX;

/**
 * <p>
 * 点赞记录表 服务实现类
 * </p>
 *
 * @author wxh
 * @since 2024-08-26
 */
@Service
@RequiredArgsConstructor
public class LikedRecordServiceImpl extends ServiceImpl<LikedRecordMapper, LikedRecord> implements ILikedRecordService {
    private final StringRedisTemplate redisTemplate;
    private final RabbitMqHelper mqHelper;

    /**
     * 点赞或取消点赞
     *
     * @param likeRecordFormDTO
     */
    @Override
    public void likeOrUnlike(LikeRecordFormDTO likeRecordFormDTO) {
        //1.新增点赞或取消点赞
        boolean isSuccess = likeRecordFormDTO.getLiked() ? like(likeRecordFormDTO) : unLike(likeRecordFormDTO);
        //2.判断返回值
        if (!isSuccess) {
            //2.1失败则直接结束
            return;
        }
        //成功
        //3.1点赞成功则统计点赞数
        Long likeTimes = redisTemplate.opsForSet()
                .size(LikeRecordConstans.LIKE_KEY_PRIFIX + likeRecordFormDTO.getBizId());
        if (ObjectUtil.isNotEmpty(likeTimes)) {
            //3.2将点赞总数存入redis
            //使用set存储，大key：业务id类型 ，小key：bizId ,value：点赞数
            //3.3组装大key和value：bizId
            String key = LIKE_TIME_TYPE_PRIFIX + likeRecordFormDTO.getBizType();
            String bizId = likeRecordFormDTO.getBizId().toString();
            redisTemplate.opsForZSet().add(key, bizId, likeTimes);
        }

    }

    /**
     * 查询用户是否点赞
     *
     * @param bizIds
     * @return 返回用户点过赞的业务id
     */
    @Override
    public Set<Long> isLikeStatusAndUser(List<Long> bizIds) {
        //1.去redis中的set集合拿bizId对应的userId是否点赞
        List<Object> pipelinedList = redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
            StringRedisConnection connect = (StringRedisConnection) connection;
            for (Long bizId : bizIds) {
                connect.sIsMember(LikeRecordConstans.LIKE_KEY_PRIFIX + bizId, UserContext.getUser().toString());
            }
            return null;
        });
        //2.数据转换并返回
        return IntStream.range(0, pipelinedList.size())
                .filter(i -> (Boolean) pipelinedList.get(i))
                .mapToObj(i -> bizIds.get(i)).collect(Collectors.toSet());
    }


    /**
     * 点赞
     *
     * @param likeRecordFormDTO
     * @return
     */
    private boolean like(LikeRecordFormDTO likeRecordFormDTO) {
        //使用set存储，key：bizId ,value：userId
        //1。构建大key
        String key = LikeRecordConstans.LIKE_KEY_PRIFIX + likeRecordFormDTO.getBizId();
        //1.2存储的value：userId
        String userId = UserContext.getUser().toString();
        //1.3存入set中
        Long count = redisTemplate.opsForSet().add(key, userId);

        return count != null && count > 0;

    }

    /**
     * 取消点赞
     *
     * @param likeRecordFormDTO
     */
    private boolean unLike(LikeRecordFormDTO likeRecordFormDTO) {
        //使用set存储，key：bizId ,value：userId
        //1。构建大key
        String key = LikeRecordConstans.LIKE_KEY_PRIFIX + likeRecordFormDTO.getBizId();
        //1.2存储的value：userId
        String userId = UserContext.getUser().toString();
        //1.3存入set中
        Long count = redisTemplate.opsForSet().remove(key, userId);

        return count != null && count > 0;

    }

    /**
     * 定时任务通知likedRecordService拉取redis中业务类型对应的bizId对应的点赞数
     *
     * @param likeType   点赞的业务类型
     * @param maxBizSize 一次拉取业务id下的的最大条数
     */
    @Override
    public void pullRedisBizTypeAndLikeTimeTask(String likeType, Integer maxBizSize) {
        //1.批量读取redis中zset里面的业务id对应点赞数量
        Set<ZSetOperations.TypedTuple<String>> typedTuples = redisTemplate.opsForZSet().popMin(LIKE_TIME_TYPE_PRIFIX + likeType, maxBizSize);
        while (ObjectUtil.isNotEmpty(typedTuples)) {
            //1.1创建点赞Bizids对应的点赞数量集合
            List<LikedTimesDTO> message = new ArrayList<>(typedTuples.size());
            for (ZSetOperations.TypedTuple<String> typedTuple : typedTuples) {
                //1.3封装数据
                String bizId = typedTuple.getValue();
                Double likeTimes = typedTuple.getScore();
                if (bizId == null || likeTimes == null) {
                    continue;
                }
                LikedTimesDTO likedTimesDTO = LikedTimesDTO.of(Long.valueOf(bizId), likeTimes.intValue());
                message.add(likedTimesDTO);
            }
            if (ObjectUtil.isNotEmpty(message)) {
                //2.发送消息
                mqHelper.send(LIKE_RECORD_EXCHANGE, StringUtils.format(LIKED_TIMES_KEY_TEMPLATE, likeType), message);
            }
            //3.继续拉取下一轮点赞数
            typedTuples = redisTemplate.opsForZSet().popMin(LIKE_TIME_TYPE_PRIFIX + likeType, maxBizSize);
        }

    }
}
